/*
	Copyright (c) 2010 <copyright holders>
	This source code is licensed under The MIT License,
	see the file LICENSE for details.
	*/

#include "Server.hpp"

#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>

#include "HelpDefs.hpp"
#include "ServerLogic.hpp"


namespace spelprog2 {
namespace net {

using namespace boost::asio::ip;

Server::Server(unsigned port, const std::string & password, bool hasLocalUser, const std::string & username) : Net(true),
    acceptor(getService(),
    tcp::endpoint(tcp::v4(), port)),
    internalRegistry_(),
    hasLocal_(hasLocalUser)
{
    getVars().setVar(SERVER_ID, "name", "Unnamed Server");
    getVars().setVar(SERVER_ID, "password", password);

    if (hasLocal_) {
        getVars().addUser(FIRST_FREE_ID);
        getVars().setVar(FIRST_FREE_ID, "name", username);
        getVars().setVar(FIRST_FREE_ID, "logged_in", "yes");
        getVars().setVar(FIRST_FREE_ID, "player", "0"); //TODO: remove, just for test
        getVars().setVar(SERVER_ID, "name", username + "'s Server");
    }

    using namespace server_logic;

    internalRegistry_.addUnhandledMsgListener(                  net::NetMessageListener::Pointer(new PassOn(*this)));
    internalRegistry_.addMsgListener<netmsg::LoginRequest>(     net::NetMessageListener::Pointer(new HandleLogin(*this)));
    internalRegistry_.addMsgListener<netmsg::ServerVarChange>(  net::NetMessageListener::Pointer(new HandleVarChange(*this)));

    startAccept();
}

Server::~Server()
{
}

void Server::startAccept()
{
    Connection::Pointer conn = Connection::create(getService(), this, internalRegistry_);
    acceptor.async_accept(conn->getSocket(), boost::bind(&Server::handleAccept, this, conn,
        boost::asio::placeholders::error));
}

void Server::handleAccept(Connection::Pointer conn, const boost::system::error_code & err)
{
    if (err) {
        return;
    }

    UserId id = getVars().addUser();

    peers[id] = conn;
    conn->start(id);

    startAccept();

    netlog << "User has joined and has been assigned ID " + boost::lexical_cast<std::string>(id);
}

void Server::removePeer(UserId id)
{
    PeerMap::iterator it = peers.find(id);
    if (it == peers.end()) {
        return;
    }
    
    //TODO: send some "close" signal?
    peers.erase(it);

    getVars().removeUser(id);
}

UserId Server::getMyClientId() const
{
    return FIRST_FREE_ID;
}

void Server::sendToUser(const Message & msg, UserId receiver)
{
    if (receiver == SERVER_ID || (hasLocal_ && receiver == 1)) {
        //TODO: verify correctness of this
        MessagePtr copy(msg.New());
        copy->CopyFrom(msg);
        const NetMessageInfo info = { this, receiver == SERVER_ID? 1 : SERVER_ID };
        postMsg(copy, info);
        return;
    }

    PeerMap::iterator it = peers.find(receiver);
    if (it == peers.end()) {
        return;
    }

    it->second->sendMessage(msg);
}

void Server::sendToAll(const Message & msg)
{
    for (PeerMap::iterator it = peers.begin(), end = peers.end(); it != end; ++it) {
        it->second->sendMessage(msg);
    }

    //always send a local copy to self
    MessagePtr copy(msg.New());
    copy->CopyFrom(msg);
    const NetMessageInfo info = { this, SERVER_ID };
    postMsg(copy, info);
}

}; //namespace net
}; //namespace spelprog2
